package org.jradweb.core.controller;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jradweb.core.utils.StringUtils;
import org.jradweb.core.web.CommonAttributes;
import org.jradweb.core.web.JFlashScope;
import org.jradweb.core.web.JHttpServletRequestWrapper;
import org.jradweb.core.web.UrlMapResolver;
import org.jradweb.core.web.WebDataBinder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import org.springframework.web.servlet.view.RedirectView;

/**
 * 
 * 
 * Created On 2008-4-24
 * 
 * @author <a href="mailto:wagzhi@gmail.com">Paul Wang</a>
 * @version $Id: AbstractMultiActionController.java 45600 2008-05-14 08:27:21Z
 *          paul.wang $
 * @param <ID>
 */
public abstract class AbstractMultiActionController<ID> extends
		MultiActionController implements JController{
	public static final String FLASH_SCOPE = "session_flash_scope";
	public static final String REQUEST_URL = "request_url";
	public static final String REQUEST_QUERYSTRING = "request_queryString";
	public static final String REQUEST_URL_WITH_QUERYSTRING = "request_url_with_queryString";

	private int pageSize = 10;
	private String name;
	// protected List<String> flash=new ArrayList<String>();

	@Autowired
	protected WebDataBinder webDataBinder;

	public WebDataBinder getWebDataBinder() {
		return webDataBinder;
	}

	public void setWebDataBinder(WebDataBinder webDataBinder) {
		this.webDataBinder = webDataBinder;
	}

	private org.jradweb.core.web.DefaultUrlMapResolver urlMapResolver;

	public org.jradweb.core.web.DefaultUrlMapResolver getUrlMapResolver() {
		return urlMapResolver;
	}

	public void setUrlMapResolver(
			org.jradweb.core.web.DefaultUrlMapResolver urlMapResolver) {
		this.urlMapResolver = urlMapResolver;
	}

	private String getActionName(HttpServletRequest request) throws Exception {
		String methodName = null;
		Map<String, String> urlParams = (Map<String, String>) request
				.getAttribute(CommonAttributes.REQUEST_URL_PARAMETERS);
		if (urlParams == null) {
			methodName = this.getMethodNameResolver().getHandlerMethodName(
					request);
		} else {
			methodName = urlParams.get(CommonAttributes.ACTION_NAME);
		}
		return methodName;
	}

	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		String actionName = this.getActionName(request);
		String methodName = null;
		if (actionName.indexOf("_") == 0) { // if action name like "_create",
											// use method name "create"
			methodName = actionName.substring(1);
		} else {
			methodName = actionName;
		}

		String requestUrl = request.getContextPath() + request.getServletPath();
		Map<String, Object> modelMap = new HashMap<String, Object>();
		// WebApplicationContext webAppCtx = (WebApplicationContext) request
		// .getSession()
		// .getServletContext()
		// .getAttribute(
		// WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
		// Map maps = webAppCtx.getBeansOfType(UrlMapResolver.class);
		// if (!maps.isEmpty()) {
		// request.setAttribute(
		// CommonAttributes.REQUEST_ATTRIBUTE_URL_MAP_RESOLVER, maps
		// .values().iterator().next());
		// }

		String urlWithQueryString = request.getQueryString() == null ? requestUrl
				: (requestUrl + "?" + request.getQueryString());
		request.setAttribute(REQUEST_QUERYSTRING, request.getQueryString());
		request.setAttribute(REQUEST_URL, requestUrl);
		request.setAttribute(REQUEST_URL_WITH_QUERYSTRING, urlWithQueryString);

		modelMap.put(REQUEST_QUERYSTRING, request.getQueryString());
		modelMap.put(REQUEST_URL, requestUrl);
		modelMap.put(REQUEST_URL_WITH_QUERYSTRING, urlWithQueryString);
		Collection<JMultiActionController> controllers = this.getControllers(
				request).values();
		modelMap.put("controllers", controllers);
		modelMap.put("flash", this.getFlash(request));
		modelMap.put("controller", this.getName());
		JHttpServletRequestWrapper requestWrapper = new JHttpServletRequestWrapper(
				request);
		ModelAndView mv = null;
		try {
			mv = invokeNamedMethod(methodName, requestWrapper, response);
		} catch (NoSuchRequestHandlingMethodException e) {
			mv = invokeNamedMethod("defaultMethod", requestWrapper, response);
		}

		// for redirect view
		if (mv.getView() != null && RedirectView.class.isInstance(mv.getView())) {
			return mv;
		} else if (mv.getViewName() == null) { // get default view name
			String viewName = this.getControllerName() + "/" + actionName;
			if (this.getUrlMapResolver() != null&&UrlMapResolver.PAGE_TYPE_JS == this.getPageType(request)) {
					viewName = "js:" + viewName;
					viewName=this.getControllerName() + "/js_" + actionName;				 
			}
			else{
				//only when view is not js and not redirect, next the flash
				getFlash(request).next();
			}
			mv.setViewName(viewName);
		}
		// for render view
		mv.getModel().putAll(modelMap);
		return mv;
	}

	private int getPageType(HttpServletRequest request) {
		String servletPath = request.getServletPath();
		if (servletPath != null
				&& servletPath.endsWith("."
						+ this.getUrlMapResolver().getJsFormat())) {
			return UrlMapResolver.PAGE_TYPE_JS;
		} else {
			return UrlMapResolver.PAGE_TYPE_HTML;
		}
	}

	public Map<String, Object> defaultMethod(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		Map<String, Object> map = new HashMap<String, Object>();
		return map;
	}

	public JFlashScope getFlash(HttpServletRequest request) {
		JFlashScope flash = (JFlashScope) request.getSession(true)
				.getAttribute(FLASH_SCOPE);
		if (flash == null) {
			flash = new JFlashScope();
			request.getSession().setAttribute(FLASH_SCOPE, flash);
		}
		return flash;
	}

	public AbstractMultiActionController() {
		super();
	}

	public ModelAndView render(String view, Map<String, Object> params) {
		return new ModelAndView(this.getControllerName() + "/" + view, params);
	}

	public ModelAndView render(HttpServletRequest request, String view,
			Map<String, Object> params) {
		params.put("controllers", this.getControllers(request).values());
		return new ModelAndView(this.getControllerName() + "/" + view, params);
	}

	public String getControllerName() {
		String name = this.getClass().getName();
		name = name.substring(name.lastIndexOf('.') + 1);
		name = name.substring(0, name.lastIndexOf("Controller"));
		return StringUtils.lowerCaseFirst(name);
	}

	public String getName() {
		return name.toLowerCase();
	}

	public void setName(String name) {
		this.name = name;
	}

	public Map<String, JMultiActionController> getControllers(
			HttpServletRequest request) {
		WebApplicationContext ctx = this.getApplicationContext(request);
		Map<String,JMultiActionController> controllers= ctx.getBeansOfType(JController.class);
			
		return controllers;
	}

	/**
	 * redirect to uri without query string
	 * 
	 * @param controller
	 * @param action
	 * @param uriParams
	 *            paramter mapped to uri
	 * @return
	 */
	public ModelAndView redirect(String controller, String action,
			Map<String, Object> uriParams) {
		return this.redirect(controller, action, uriParams, null);
	}

	public ModelAndView redirect(String controller, String action,
			Map<String, Object> uriParams, Map<String, String> params) {
		if(this.urlMapResolver==null){
			if(uriParams==null){
				uriParams=new HashMap<String,Object>();
			}
			if(params!=null){
				uriParams.putAll(params);
			}
			String servletUrl="/"+controller+"/"+action+".html";
			return new ModelAndView(new RedirectView(servletUrl, true), uriParams);
		}
		String servletPath = this.getServletPath(controller, action, uriParams);
		return new ModelAndView(new RedirectView(servletPath, true), params);
	}

	public ModelAndView redirect(String action, Map<String, Object> uriParams) {
		return this.redirect(this.getName(), action, uriParams);
	}

	public ModelAndView redirect(String action) {
		return this.redirect(this.getName(), action, null);
	}

	public ModelAndView redirect(String action, Map<String, Object> uriParams,
			Map<String, String> params) {
		return this.redirect(this.getName(), action, uriParams, params);
	}

	/**
	 * get servlet path by controller and action and uriParams
	 * 
	 * @param controller
	 * @param action
	 * @param uriParams
	 *            params mapped in uri
	 * @return
	 */
	private String getServletPath(String controller, String action,
			Map<String, Object> uriParams) {
		if (uriParams == null) {
			uriParams = new HashMap<String, Object>();
		}
		if(this.urlMapResolver!=null){
			uriParams.put(CommonAttributes.CONTROLLER_NAME, controller);
			uriParams.put(CommonAttributes.ACTION_NAME, action);
	
			String servletPath = this.urlMapResolver.getServletPath(uriParams);
		return servletPath;
		}else{
			return "/"+controller+"/"+action+".html";
		}
	}

	/**
	 * get spring application context
	 * 
	 * @param pageContext
	 * @return
	 */
	public WebApplicationContext getApplicationContext(
			HttpServletRequest request) {
		return (WebApplicationContext) request
				.getSession()
				.getServletContext()
				.getAttribute(
						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public abstract ID getId(HttpServletRequest request);
}
